package org.dromara.common.mail.utils;

import cn.hutool.core.util.ArrayUtil;
import jakarta.mail.internet.AddressException;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeUtility;

import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


/**
 * @Author 刘武贵
 * @Date 2024/8/15 22:00
 * @Description 功能描述：邮件内部工具类
 */
public class InternalMailUtil {

    /**
     * 将多个字符串邮件地址转为{@link InternetAddress}列表<br>
     * 单个字符串地址可以是多个地址合并的字符串
     * 从字符串数组中解析出Internet地址
     *
     * 此方法接收一个地址字符串数组和一个字符集作为输入，将每个字符串解析为一个或多个Internet地址
     * 如果解析结果不为空，则将其添加到结果列表中最终，该方法返回一个包含所有解析得到的地址的数组
     *
     * @param addrStrs 地址数组 地址字符串数组
     * @param charset  编码（主要用于中文用户名的编码） 用于解析地址的字符集
     * @return 地址数组 包含所有解析得到的Internet地址的数组
     * @since 4.0.3
     */
    public static InternetAddress[] parseAddressFromStrs(String[] addrStrs, Charset charset) {
        // 初始化结果列表，初始容量为地址字符串数组的长度
        final List<InternetAddress> resultList = new ArrayList<>(addrStrs.length);
        InternetAddress[] addrs;
        // 遍历地址字符串数组
        for (String addrStr : addrStrs) {
            // 解析单个地址字符串
            addrs = parseAddress(addrStr, charset);
            // 如果解析结果不为空，则将其添加到结果列表中
            if (ArrayUtil.isNotEmpty(addrs)) {
                Collections.addAll(resultList, addrs);
            }
        }
        // 将结果列表转换为InternetAddress数组并返回
        return resultList.toArray(new InternetAddress[0]);
    }

    /**
     * 解析字符串地址为InternetAddress对象，仅返回第一个解析出的地址
     *
     * @param address 地址字符串 待解析的地址字符串
     * @param charset 编码，{@code null}表示使用系统属性定义的编码或系统编码 解析时使用的字符集
     * @return 地址列表 解析出的第一个InternetAddress对象
     * @throws MailException 当地址为空或无法解析时抛出
     */
    public static InternetAddress parseFirstAddress(String address, Charset charset) {
        // 尝试解析传入的地址字符串，可能得到多个InternetAddress对象
        final InternetAddress[] internetAddresses = parseAddress(address, charset);

        // 如果解析结果为空数组，尝试直接使用传入的地址字符串创建InternetAddress对象
        if (ArrayUtil.isEmpty(internetAddresses)) {
            try {
                return new InternetAddress(address);
            } catch (AddressException e) {
                // 如果地址字符串无法解析为InternetAddress，转换异常并抛出
                throw new MailException(e);
            }
        }

        // 返回解析出的第一个地址
        return internetAddresses[0];
    }

    /**
     * 将一个地址字符串解析为多个地址<br>
     * 地址间使用" "、","、";"分隔
     *
     * @param address 地址字符串 要解析的邮件地址字符串
     * @param charset 编码，{@code null}表示使用系统属性定义的编码或系统编码 指定的字符集，用于编码用户名部分
     * @return 地址列表 InternetAddress数组，包含解析后的邮件地址
     * @throws MailException 如果解析地址或编码用户名时发生错误
     */
    public static InternetAddress[] parseAddress(String address, Charset charset) {
        // 用于存储解析后的邮件地址
        InternetAddress[] addresses;
        try {
            // 使用JavaMail的InternetAddress类解析邮件地址
            addresses = InternetAddress.parse(address);
        } catch (AddressException e) {
            // 如果解析邮件地址失败，抛出MailException异常
            throw new MailException(e);
        }
        // 编码用户名
        if (ArrayUtil.isNotEmpty(addresses)) {
            // 获取字符集名称，如果charset为null，则charsetStr为null
            final String charsetStr = null == charset ? null : charset.name();
            for (InternetAddress internetAddress : addresses) {
                // 重新设置邮件地址的用户名部分，使用指定的字符集进行编码
                try {
                    internetAddress.setPersonal(internetAddress.getPersonal(), charsetStr);
                } catch (UnsupportedEncodingException e) {
                    // 如果字符集不支持，抛出MailException异常
                    throw new MailException(e);
                }
            }
        }

        // 返回解析后的邮件地址数组
        return addresses;
    }

    /**
     * 编码中文字符<br>
     * 编码失败返回原字符串
     *
     * @param text    被编码的文本
     * @param charset 编码
     * @return 编码后的结果
     */
    public static String encodeText(String text, Charset charset) {
        try {
            // 使用MimeUtility的encodeText方法对文本进行编码，参数为待编码文本，字符集名称和null
            return MimeUtility.encodeText(text, charset.name(), null);
        } catch (UnsupportedEncodingException e) {
            // 忽略UnsupportedEncodingException异常，因为charset参数通常保证了字符集是支持的
        }
        return text;
    }
}
